<?php


namespace App\Repositories;


use App\Models\BaseModel;
use App\Utils\Output;
use Closure;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Throwable;

abstract class BaseRepository
{
    function __construct()
    {
        $this->model = $this->getModel();
    }

    abstract function getModel(): BaseModel;

    /**
     * @var BaseModel
     */
    protected $model;

    /**
     * 分页查询
     * @param array $params
     * @return Output
     */
    function adminPager(array $params)
    {
        extract($params);
        $query = $this->model->newQuery();
        $totalQuery = clone $query;
        $total = $totalQuery->count();
        $lis = $this->setParams($query, $params, 'id');
        return Output::pager($lis, $total);
    }

    /**
     * 删除
     * @param int $id 编号
     * @return Output
     * @throws Throwable
     */
    function delete(int $id)
    {
        try {
            DB::beginTransaction();
            $model = $this->model->find($id);
            $model->delete();
            $this->deleteAfter([$id]);
            DB::commit();
            return Output::success(__('common.tips.successfully_deleted'));
        } catch (Throwable $exception) {
            DB::rollback();
            return Output::error($exception, __('common.tips.deletion_failed'));
        }
    }

    /**
     * 批量删除
     * @param $ids
     * @return Output
     * @throws Throwable
     */
    function batchDelete($ids)
    {
        if (empty($ids)) {
            return Output::fail(__('common.tips.please_select_the_data_to_be_deleted'));
        }
        if (is_string($ids)) {
            $ids = explode(',', $ids);
        } else if (is_numeric($ids)) {
            $ids = [$ids];
        }
        try {
            DB::beginTransaction();
            $updateCount = $this->model->newQuery()
                ->whereIn('id', $ids)
                ->update([
                    'deleted_at' => time()
                ]);
            if ($updateCount == count($ids)) {
                $this->deleteAfter($ids);
                DB::commit();
                return Output::success(__('common.tips.successfully_deleted'));
            }
            DB::rollback();
            return Output::fail(__('common.tips.deletion_failed'));
        } catch (Throwable $exception) {
            DB::rollback();
            return Output::error($exception, __('common.tips.deletion_failed'));
        }
    }

    /**
     * 获取对象详情
     * @param int $id
     * @return Output
     */
    function detail(int $id)
    {
        try {
            $entity = $this->model->find($id);
            if (!empty($entity)) {
                unset($entity['password']);
                return Output::data($entity);
            }
            return Output::fail(__('common.tips.data_does_not_exist'));
        } catch (Throwable $exception) {
            return Output::error($exception, __('common.tips.get_data_exception'));
        }
    }

    /**
     * 保存数据
     * @param array $data 数据数组
     * @param int $id 主键 0 新增
     * @return Output
     */
    function save(array $data, int $id = 0): Output
    {
        if ($id) {
            $model = $this->model::find($id);
            if (empty($model)) {
                return Output::fail(__('common.tips.data_does_not_exist'), $id);
            }
            $model->fill($data)->save();
            return Output::success(__('common.tips.modified_successfully'), $id);
        } else {
            $this->model->fill($data)->save();
            return Output::success(__('common.tips.added_successfully'), $this->model['id']);
        }
    }

    //#region protected


    /**
     * 删除后执行的方法
     * @param array $ids
     */
    protected function deleteAfter(array $ids)
    {

    }

    /**
     *
     * @param Builder $query
     * @param array $params
     * @param string $defaultSoftField
     * @param false $pager
     * @return mixed
     */
    protected function setParams($query, array $params, $defaultSoftField = 'id', $pager = false)
    {

        $field = Arr::get($params, 'field', '');
        if (is_array($defaultSoftField)) {

            if (!empty($field)) {
                $query->orderBy($field, Arr::get($params, 'order', 'desc'));
            }
            foreach ($defaultSoftField as $key => $item) {
                if ($field == $key) {
                    continue;
                }
                $query->orderBy($key, $item);
            }

        } else {
            if (empty($field) || $field == $defaultSoftField) {
                $query->orderBy($defaultSoftField, Arr::get($params, 'order', 'desc'));
            } else {
                $query->orderBy($field, Arr::get($params, 'order', 'desc'))->orderBy($defaultSoftField, 'desc');
            }

        }
        if (Arr::get($params, 'export', 0)) {
            return $query;
        }
        $limit = Arr::get($params, 'limit', 20);
        $page = Arr::get($params, 'page', 1);
        if ($pager) {
            return $query->paginate($limit);
        }
        if (empty($page)) {
            return $query->get();
        }
        return $query->skip(($page - 1) * $limit)->take($limit)->get();
    }

    /**
     * @param $query
     * @param $params
     * @param string $defaultSoftField
     * @return Output
     */
    protected function queryToPager($query, $params, $defaultSoftField = 'id'): Output
    {
        try {
            $totalQuery = clone $query;
            $total = $totalQuery->count();
            $lis = $this->setParams($query, $params, $defaultSoftField);
            return Output::pager($lis, $total);
        } catch (Throwable $exception) {
            return Output::error($exception, __('common.tips.get_data_exception'));
        }
    }

    /**
     * @param $query
     * @return Output
     */
    protected function queryToList($query): Output
    {
        try {
            $lis = $query->select();
            return Output::data($lis);
        } catch (Throwable $exception) {
            return Output::error($exception, __('common.tips.get_data_exception'));
        }
    }

    /**
     * api查询
     * @param Collection $collect
     * @param $params
     * @param string $defaultSoftField
     * @param Closure|null $resultHandler
     * @return array|mixed
     */
    protected function collectionPageQuery($collect, $params, $defaultSoftField = 'id', Closure $resultHandler = null)
    {
        $collect = $this->collectOrderByParams($collect, $params, $defaultSoftField);
        extract($params);
        $view = Arr::get($params, 'view', 'pager');
        if (!empty($table_where) || !empty($table_search) || ($view != 'pager') || !empty($status_statistic_field)) {
            if (!empty($table_where)) {
                $collect = $this->collectTableWhere($collect, $table_where);
            }
            if ($view == 'count') {
                return Output::data($collect->count());
            }
            if (!empty($table_search)) {
                $filed = Arr::get($table_search, 'field', '');
                $keyword = Arr::get($table_search, 'keyword', '');
                if (!empty($filed)) {
                    if (!empty($keyword))
                        $collect = $collect->filter(function ($item) use ($filed, $keyword) {
                            return strpos($item[$filed], $keyword) !== false;
                        });
                    $group = [];
                    $collect->groupBy($filed)->map(function ($item, $key) use ($filed, &$group) {
                        $group[] = [
                            $filed => $key,
                            'count' => $item->count(),
                        ];
                    });
                    $output = Output::data($group);
                    if ($resultHandler instanceof Closure) {
                        $output = $resultHandler('table_search', $output);
                    }
                    $output->view = 'table_search';
                    $output->filed = $filed;
                    return $output;
                }
            }
            if (!empty($status_statistic_field)) {
                $output = $this->statusStatisticsCollect($collect, $status_statistic_field);
                if ($resultHandler instanceof Closure) {
                    $output = $resultHandler('status_statistics', $output);
                }
                $output->view = 'status_statistics';
                return $output;
            }
        }
        $total = $collect->count();
        if (!Arr::get($params, 'export', 0)) {
            $limit = Arr::get($params, 'limit', 20);
            $page = Arr::get($params, 'page', false);
            if ($page !== false) {
                $collect = $collect->slice(($page - 1) * $limit, $limit);
            }
        }
        $output = Output::data($collect->values());
        if ($resultHandler instanceof Closure) {
            $output = $resultHandler('pager', $output);
        }
        if (!isset($output->count)) {
            $output->count = $total;
            $output->view = 'pager';
        }
        return $output;
    }

    protected function statusStatisticsCollect($collect, $statusField): Output
    {
        $all = 0;
        $result = [];
        $tj = [];
        $collect->map(function ($item) use ($statusField, &$tj) {
            $tj[$item[$statusField]]++;
        });
        foreach ($tj as $k => $count) {
            $all += $count;
            $result[] = [
                'status' => $k,
                'count' => $count
            ];
        }
        $result[] = [
            'status' => 'all',
            'count' => $all
        ];
        return Output::data($result);
    }

    protected function collectTableWhere($collect, $wheres)
    {
        if (is_string($wheres[0])) {
            return $collect->where($wheres[0], $wheres[1], $wheres[2]);
        } else {
            foreach ($wheres as $where) {
                $collect = $this->collectTableWhere($collect, $where);
            }
        }
        return $collect;
    }

    /**
     * 设置排序
     *
     * @param Collection $collect
     * @param       $params
     * @param string $defaultSoftField
     * @param array $fieldMap
     * @return mixed
     */
    protected function collectOrderByParams(Collection $collect, $params, $defaultSoftField = 'id', $fieldMap = [])
    {
        $field = Arr::get($params, 'field', '');
        if (array_key_exists($field, $fieldMap)) {
            $field = $fieldMap[$field];
        }

        $sort = Arr::get($params, 'order', 'desc');
        if (empty($field) || $field == $defaultSoftField) {
            if (!empty($defaultSoftField)) {
                if ($sort == 'desc') {
                    $collect = $collect->sortBy($defaultSoftField);
                } else {
                    $collect = $collect->sortByDesc($defaultSoftField);
                }
            }
        } else {
            if ($sort == 'desc') {
                $collect = $collect->sortBy($field);
            } else {
                $collect = $collect->sortByDesc($field);
            }
        }
        return $collect;
    }
    //#endregion
}
